#include "emb_timer.h"
#include "emb_pqueue.h"
enum
{
    EMB_TIMER_STATUS_IDLE = 0,
    EMB_TIMER_STATUS_RUNNING = 1,
};

emb_pqueue_ctrl_t timer_queue;

int8_t timer_cmp(const emb_timer_ctrl_t * First, const emb_timer_ctrl_t * Second)
{
    size_t tick = EMB_PORT_TICK_GET();
    size_t diff1=tick-First->Last;
    size_t diff2 = tick - Second->Last;;
    if (diff1>First->Period)
    {
        if(diff2>Second->Period)
        {
            return diff1 - First->Period > diff2 - Second->Period ? 1 : -1;
        }
        else{
            return 1;
        }
    }
    else if(diff2>Second->Period)
    {
        return 1;
    }
    else{
        return First->Period - diff1 > Second->Period - diff2 ? -1 : 1;//
    }
}

void emb_timer_prev_init(uint8_t* Buf,size_t BufSize)
{
    emb_pqueue_init(&timer_queue, Buf, BufSize, sizeof(emb_timer_ctrl_t *),timer_cmp);
}

void emb_timer_init(emb_timer_ctrl_t *Timer, size_t Period, uint8_t Mode,uint32_t Count, emb_timer_callback_fn_t CallBack)
{
    Timer->CallBack = CallBack;
    Timer->Last = 0;
    Timer->Period = Period;
    Timer->Count = Count;
    Timer->Status = EMB_TIMER_STATUS_IDLE;
    Timer->Mode = Mode;
}

void emb_timer_start(emb_timer_ctrl_t *Timer)
{
    if(Timer->Status==EMB_TIMER_STATUS_RUNNING)
    {
        return;
    }
    
    EMB_PORT_DISABLE_IRQ();
    Timer->Last = EMB_PORT_TICK_GET();
    Timer->Status = EMB_TIMER_STATUS_RUNNING;
    emb_pqueue_push(&timer_queue, Timer);
    EMB_PORT_ENABLE_IRQ();
}


void emb_timer_stop(emb_timer_ctrl_t *Timer)
{
    EMB_PORT_DISABLE_IRQ();
    Timer->Status = EMB_TIMER_STATUS_IDLE;
    EMB_PORT_ENABLE_IRQ();
}

void emb_timer_handler()
{
    emb_timer_ctrl_t *top;

    while(1)
    {
        top = emb_pqueue_head(&timer_queue);
        if(top==NULL)
        {
            break;
        }

        if(top->Status==EMB_TIMER_STATUS_IDLE)
        {
            emb_pqueue_pop(&timer_queue, top);
            continue;
        }

        if((EMB_PORT_TICK_GET()-top->Last)>=top->Period)
        {
            top->CallBack(top);
            emb_pqueue_pop(&timer_queue, top);
            if (top->Mode == EMB_TIMER_MODE_ONESHOT)
            {
                top->Count--;
                if(top->Count!=0)
                {
                    emb_timer_start(top);
                }
                else{
                    top->Status = EMB_TIMER_STATUS_IDLE;
                }
            }
            else{
                EMB_PORT_DISABLE_IRQ();

                EMB_PORT_ENABLE_IRQ();
            }
        }
        else{
            break;
        }
    }
}

size_t emb_timer_next_timeout()
{
    emb_timer_ctrl_t *timer = emb_pqueue_head(&timer_queue);
    if(timer!=NULL)
    {
        return EMB_PORT_TICK_GET() - timer->Last;
    }

    return 0xffffff;
}

